package com.lotter.invocation;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lotter.common.invocation.BaseConditionVO;



/**
 * annotation: 分页功能   实现方式 mybatis 拦截器
 * @author zhangzechi
 * 2015年5月28日 下午2:34:05
 * version :1.0.0
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PagePlugin implements Interceptor{

	 protected final Logger logger = LoggerFactory.getLogger(PagePlugin.class);
	 
	private static String dialect = "mysql"; //mapper.xml中需要拦截的ID(正则匹配)
	private static String PrepageSqlId = ".*ByPage$"; //mapper.xml中需要拦截的ID(正则匹配) 默认值
	
	/**
	 * 拦截器执行过程
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		//StatementHandler的默认实现类是RoutingStatementHandler，因此拦截的实际对象是它
		 if(!(invocation.getTarget() instanceof RoutingStatementHandler))   
         return invocation.proceed(); 
		 RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();
		 MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
				 SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY);
		 MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
		 String id = mappedStatement.getId();
		 //匹配需要分页的查询
		 if(!id.matches(PrepageSqlId)){
			 return invocation.proceed();
		 }
		 // 拦截需要分页的SQL
		BoundSql boundSql = statementHandler.getBoundSql();
		// 原始的SQL语句
		Object parameterObject = boundSql.getParameterObject();// 分页SQL<select>中parameterType属性对应的实体参数，即Mapper接口中执行分页方法的参数,该参数不得为空
		if (parameterObject == null) {
			throw new NullPointerException("parameterObject尚未实例化！");
		} else {
			String sql = boundSql.getSql();
			//查询总数
			String countSql = "select count(1) from (" + sql + ") a";
			//Connection 表示与一个特定数据库的会话
			Connection connection = (Connection)invocation.getArgs()[0];
			PreparedStatement countStatement = connection.prepareStatement(countSql);
			ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
			parameterHandler.setParameters(countStatement);
			ResultSet rs = countStatement.executeQuery();
			int count = 0;
			if (rs.next()) {
				count = rs.getInt(1);
			}
			rs.close();
			countStatement.close();
			StringBuilder pageSql = new StringBuilder();
			if (parameterObject instanceof BaseConditionVO){
				BaseConditionVO vo = (BaseConditionVO) parameterObject;
				vo.setTotalCount(count);
				pageSql.append(getLimitString(sql, vo.getStartIndex(),vo.getEndIndex()));
			}else{
				throw new NoSuchFieldException(parameterObject
						.getClass().getName() + "不存在 page 属性！");
			}
			//重新执行
			metaObject.setValue("delegate.boundSql.sql", pageSql.toString());
		}
	   // 将执行权交给下一个拦截器
       return invocation.proceed();
	}

	/**
	 * 当目标类是StatementHandler类型时，才包装目标类，否者直接返回目标本身,减少目标被代理的次数
	 */
	@Override
	public Object plugin(Object target) {
		if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
	}

	/**
	 * 
	 */
	@Override
	public void setProperties(Properties properties) {
		if(StringUtils.isNotBlank(properties.getProperty("dialect"))){
			dialect = properties.getProperty("dialect");
			PrepageSqlId =properties.getProperty("pageSqlId");
		}
	}
	
	/**
	 * 生成特定的分页sql
	 * @param sql
	 * @param page
	 * @return
	 */
	public String getLimitString(String sql,int startNum,int endNum) {
		StringBuffer pageSql = new StringBuffer(sql.length()+100);  
		if("mysql".equals(dialect)){
			 pageSql.append(sql);  
             pageSql.append(" limit "+startNum+","+endNum); 
		}else if("oracle".equals(dialect)){
			pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
    		pageSql.append(sql);
    		pageSql.append(") tmp_tb where ROWNUM<=");
    		pageSql.append(endNum);
    		pageSql.append(") tmp_123 where row_id>");
    		pageSql.append(startNum); 
		}
		return pageSql.toString();  
	}

}
